<?php

/**
 * Kao/App.php
 *
 * Kao: PHP Web Framework <http://www.byhuy.com/kao/>.
 * Copyright 2007-2008, Huy Nguyen <http://www.byhuy.com/>.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 */


/**
 * The Kao application handler class
 *
 * Singleton
 *
 * @author Huy Nguyen <http://www.byhuy.com/>
 * @since 0.1
 *
 */

class Kao_App
{
    
    /**
     * The list of component types that Kao supports
     */
    const COMP_VENDOR    = 'vendor';
    const COMP_FORM      = 'form';
    const COMP_HELPER    = 'helper';
    const COMP_RECORD    = 'record';
    const COMP_VALIDATOR = 'validator';
    const COMP_DAO       = 'dao';
    
    /**
     * Component directory name
     * 
     * Define the list of component paths
     * 'component_type_name' => 'dir_name';
     */
    public static $componentDirs = array (
        self::COMP_VENDOR    => 'vendors', 
        self::COMP_FORM      => 'forms',
        self::COMP_HELPER    => 'helpers',
        self::COMP_RECORD    => 'records',
        self::COMP_VALIDATOR => 'validators',
        self::COMP_DAO       => 'daos'
    );
    
    /**
     * Singleton instance
     *
     * @var Kao_App
     */
    protected static $_instance = null;

    
    protected $_frameworkPath = null;
    
    protected $_appsPath = null;
    
    protected $_appName = null;
    
    /**
     * Get a Kao_App singleton instance
     *
     * @return Kao_App
     */
    public static function getInstance()
    {
        if (null === self::$_instance)
        {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
    
    
    /**
     * Init the framework
     * To start the application.
     * Called in Kao.php
     * 
     * @param string $path
     * @return Kao_App
     */
    public function initFramework($path)
    {
        $this->_frameworkPath = $path;
        return $this;
    }
    
    /**
     * Inits the application to work
     * 
     * @param string $name
     * @retirm Kao_App 
     */
    public function initApplication($name = 'default')
    {
        $this->_appName = $name;
        return $this;
    }
    
    
    /**
     * Gets the path to the working application directory
     */
    public function getApplicationPath()
    {
        if (null === $this->_appName)
        {
            $this->initApplication();
        }
        return $this->_appsPath . DIRECTORY_SEPARATOR . $this->_appName;
    }
    
    
    public function getAppsPath()
    {
        if (null === $this->_appPath)
        {
            $this->setAppsPath();
        }
        return $this->_appsPath;
    }
    
    public function setAppsPath($path = null)
    {
        if (null === $path && null != $this->_frameworkPath)
        {
            $this->_appsPath = $this->_frameworkPath . DIRECTORY_SEPARATOR . 'applications';
        }
        else
        {
            $this->_appsPath = $path;
        }
        return $this;
    }
    
    // TODO move this method to Kao ?
    /**
    * Load a component
    *
    * @param string $type
    * @param string $name
    * @param boolean $checkInCommon
    * @return Kao_Component
    */
    public function loadComponent($type, $name, $checkInCommon = true)
    {
        if (!isset(Kao_App::$componentDirs[$type]))
        {
            // TODO throw exception
        }

        $filename = 'components' . DIRECTORY_SEPARATOR . Kao_App::$componentDirs[$type] . DIRECTORY_SEPARATOR . $name . '.php';

        self::requireFile($filename, $checkInCommon);

        $className = $name . ucfirst($type);

        if (!is_subclass_of($className, 'Kao_Component'))
        {
            // TODO thow exception
        }

        return new $className($this);
    }
    
    
    
    
    /**
     * Checks existent and return the full path of a file. 
     * Kao_App
     * First, checks in the application path. If the file does not exist, 
     * check in the common application path. 
     * If the file does not exist in neither two path, return false.
     * 
     * @param string $filePath
     * @param boolean $checkInCommon
     * @return mixed
     * 
     */
    public function getFullPath($filePath, $checkInCommon = true)
    {
        $paths = array();
        
        if ($this->getAppPath())
        {
            $paths[] = $this->getAppPath();
        }
        
        if ($checkInCommon && (null !== $this->_frameworkPath))
        {
            $paths[] = $this->_frameworkPath . DIRECTORY_SEPARATOR . 'common';
        }
        
        foreach ($paths as $path)
        {
            $finalPath = $path . DIRECTORY_SEPARATOR . $filePath;
            if (file_exists($finalPath) && is_readable($finalPath))
            {
                return $finalPath;
            }
        }
        return false;
    }
    
    public function requireFile($filePath, $checkInCommon = true)
    {
        $path = $this->getFullPath($filePath, $checkInCommon);
        
        if (false !== $path)
        {
            // TODO re-check this error message
            trigger_error('require_once(\'' . $filePath . '\'): failed to open stream: No such file or directory', E_USER_ERROR);
        }
        
        return require_once $path;
    }

}
